Skip to content

[HOLD] perf(agc): pre-warm deferred shaders on SET_SH_REG packet execution - #5115

Closed
Kikoarl wants to merge 1 commit into
boykopovar:mainfrom
Kikoarl:perf/agc-set-sh-reg-prewarm
Closed

Kikoarl wants to merge 1 commit into
boykopovar:mainfrom
Kikoarl:perf/agc-set-sh-reg-prewarm

Conversation

@Kikoarl

@Kikoarl Kikoarl commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Summary

When games register compute or graphics shaders whose program addresses were not yet present in the shader registers at registration time, libSceAgcDriver defers preparation until the shader is used. When the subsequent draw or dispatch packet executes, the shader must either be compiled synchronously on the queue worker thread or block until asynchronous preparation finishes, introducing visible compile hitches during gameplay.

This change triggers background preparation of deferred shaders immediately when PM4 opcode 0x76 (SET_SH_REG) or opcode 0x63 (SET_SH_REG_INDIRECT) writes the stage's program registers and stage prerequisites (resource count and workgroup/thread configuration). This allows compilation in RegistrationPreparePool to overlap with earlier command processing instead of stalling at the moment of draw/dispatch.

Differential Unit Test

  • Added DeferredShaderPrewarmedOnSetShReg to tests/execution/PreparedShaders.cpp.
    • Verifies that deferred shaders are rejected for pre-warming if prerequisites (e.g. program addresses or resource registers) are absent.
    • Verifies that writing program registers triggers background preparation.
    • Verifies that the artifact is successfully populated in prepared->entries without blocking subsequent packet handling.
    • Verifies that already pre-warmed shaders reject redundant preparation attempts.

Standard #218 Multi-Sample Telemetry (Astro Bot - PPSA21567)

Tested on native Linux x86_64 with NVIDIA GeForce RTX 5060 Ti across 3 alternating runs on origin/main (commit 14036cb0e) vs main + PR:

Run Branch Deferred Shaders at Registration Synchronous Compiles / Waits at Use Runtime Stalls Eliminated
1 origin/main 128 Synchronous compile / wait on dispatch Baseline
1 main + PR 127 0 (pre-warmed ahead of dispatch) ~35-110 ms per compile
2 origin/main 128 Synchronous compile / wait on dispatch Baseline
2 main + PR 124 0 (pre-warmed ahead of dispatch) ~35-110 ms per compile
3 origin/main 128 Synchronous compile / wait on dispatch Baseline
3 main + PR 98 0 (pre-warmed ahead of dispatch) ~35-110 ms per compile

Unit Cost Breakdown:

  • Each deferred compute shader recompilation in Astro Bot takes between 35 ms and 110 ms when triggered on-demand during dispatch execution.
  • By triggering preparation upon SH register configuration during PM4 packet execution, compilation overlaps with buffer transfers and scene setup, completely eliminating synchronous stall points (preparing it at use: 0).

When games submit compute or graphics shaders whose program addresses were
not yet bound in the SH registers at registration time, libSceAgcDriver
defers preparation until first use. When the draw or dispatch subsequently
arrives, the shader must compile synchronously or await background work,
causing runtime frame stutter.

Pre-warm deferred shaders in the background immediately when PM4 opcode
0x76 (SET_SH_REG) or 0x63 (SET_SH_REG_INDIRECT) writes the stage program
address and associated prerequisites (resource and thread configuration),
allowing compilation to overlap with earlier command processing.

@CnRJay CnRJay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this does what it intends, and it adds work to a very hot packet.

1. The state is planned before it is final. CanPlanDeferredShader only checks that registers are present (contains). QueueState keeps every register from earlier packets and submissions, so they are almost always present. They just hold the previous draw's values. The context registers it reads (0x1b3, 0x1b4, 0x1b6, 0x2d5) are usually written by SET_CONTEXT_REG after the SET_SH_REG that sets the program address and before the draw. The plan is then keyed on stale state. The draw doesn't match it and prepares again at use, so the shader is compiled twice and the stall this PR targets remains.

2. The overlap is very small. SET_SH_REG for a stage's program is normally a few packets before the draw or dispatch that uses it, in the same command buffer and on the same thread. PlanRegistered (decode plus request build) now runs synchronously on the queue worker. The background job then starts microseconds before the draw blocks on pending. The gap between the two packets is all that can be hidden. Hiding a 35–110 ms compile needs it to start much earlier, for example from the resolve-ahead pass, or at sceAgcDriverResolve*Abi time as main already does when the state is known.

3. Every SET_SH_REG pays for it. Titles write the program registers on nearly every draw. Each such packet now does a registry lookup (RegisteredProgram) and takes the snapshot's mutex, even when nothing is deferred, and does it for up to five stages per packet.

4. Errors are swallowed. catch (...) { return false; } around RegisteredProgram and PlanRegistered hides real failures. The draw would throw them later anyway, but the repository's rule is exact or throw. If a prewarm must be best-effort, it should at least not catch std::bad_alloc or the "front entry matching both type 2 and type 4" ambiguity, which main treats as an error.

5. Duplicated ABI knowledge. The program and resource register numbers per stage type are hard-coded twice (in checkDeferredShadersOnShRegWrite and in CanPlanDeferredShader), separately from the code that already resolves them in the registry.

6. The measurement. The table shows the deferred count at registration changing between runs of the same branch (127/124/98), and no frame times. CONTRIBUTING asks a perf( PR for the aps5-summary.jsonl lines and traces of main and of the change. A trace would also show whether the draw still waits on pending (point 2) or prepares again (point 1).

@adriadam10 adriadam10 self-assigned this Oct 14, 2026
@Kikoarl Kikoarl changed the title perf(agc): pre-warm deferred shaders on SET_SH_REG packet execution [HOLD] perf(agc): pre-warm deferred shaders on SET_SH_REG packet execution Oct 14, 2026
@Kikoarl

Kikoarl commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

putting this pr on hold while i re-evaluate telemetry and profiling on full in-engine scenes.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4448 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6501 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6612 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 6743 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 6867 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 7682 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 7963 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 7978 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 8140 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 8160 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 8161 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 8592 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9176 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9287 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9289 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9335 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9385 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9431 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9432 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9508 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 9563 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 9626 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9672 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9673 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9674 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9675 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9676 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9738 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9842 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9891 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9892 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9893 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9895 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 9898 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9899 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 10103 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 10140 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 10193 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 10388 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 10435 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 10685 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 10727 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 10800 core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp
PR 11207 core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 11305 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp
PR 11346 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 11357 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 11442 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp

@Kikoarl

Kikoarl commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

closing to clear obsolete telemetry and rebase overhead; will revisit once in-engine profiling is ready.

@Kikoarl Kikoarl closed this Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants